In [25]:
# STAT 415/615 Regression (M. Baron)

# Python Lab 2. Review of T-tests and F-tests

# Getting started: Set the working directory. Yours will be different from mine. 
# It should be the folder where you saved the data file from Blackboard.

import os
os.chdir(r"C:\Users\baron\Documents\Teach\615 Regression\Data")
# The `r` before the quotation marks is important because it tells Python to treat the backslashes in the Windows path literally.

# Read the data:
H = pd.read_csv("HOME_SALES.csv")

# We'll use these Python libraries:
import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
In [26]:
### 1. A one-sample T-test

### 1a. A one-sample, two-sided T-test

# There is a claim that the average price of homes in the region is $300,000. Does the data set support or disprove the claim?
# This is a two-sided test because there is no specified direction. We are simply testing whether the population mean is 300,000 or not.
# In the data, prices are measured in thousands of dollars, so we test against `300`.

stats.ttest_1samp(H["SALES_PRICE"], popmean=300)
Out[26]:
TtestResult(statistic=-3.6618839198862165, pvalue=0.00027593605743073406, df=521)
In [27]:
# The two-sided 95% confidence interval can be calculated as follows:

x = H["SALES_PRICE"]

mean_x = x.mean()
sd_x = x.std(ddof=1)
n = x.count()

se = sd_x / np.sqrt(n)
t_star = stats.t.ppf(0.975, df=n-1)

ci = (
    mean_x - t_star * se,
    mean_x + t_star * se
)

ci
Out[27]:
(266.0347984422529, 289.7534965769042)
In [28]:
# The sample mean is:

mean_x
Out[28]:
277.89414750957854
In [ ]:
### Conclusion

# The p-value is very low, hence there is significant evidence that the mean home price is not $300,000.
# We also find that the sample mean price in the data set is $277,894, the observed t-statistic is t = -3.66, 
# and the 95% confidence interval for the mean price is approximately [$266,035, $289,754].

# You may recall the duality between hypothesis testing and confidence estimation: the level α two-sided test 
# rejects the null hypothesis if and only if the `(1 − α)100%` confidence interval does not contain the tested
# parameter value. Here we see that the confidence interval does not contain $300,000, and no surprise, H₀ is rejected.
In [29]:
## 1b. A one-sample, left-tail T-test

# Is the mean price less than $300,000?
# This is a one-sided, left-tail test.

# Python's `ttest_1samp()` gives us the t-statistic and the **two-sided** p-value. To obtain the left-tail p-value, we use the t distribution:

x = H["SALES_PRICE"]

t = (x.mean() - 300) / ( x.std(ddof=1) / np.sqrt(len(x)))

p_value = stats.t.cdf(t, df=len(x)-1)

t, p_value
Out[29]:
(-3.6618839198862188, 0.00013796802871536595)
In [ ]:
# This is exactly one-half of the two-sided p-value because the observed t-statistic is in the left tail.
In [31]:
# For a one-sided 95% confidence interval, we can calculate:

upper = x.mean() + stats.t.ppf(0.95, df=len(x)-1) * ( x.std(ddof=1) / np.sqrt(len(x)))
upper
Out[31]:
287.8413935889023
In [ ]:
# Thus, the one-sided confidence interval is approximately (-Inf, 287.8414)
In [ ]:
### Conclusion

# The p-value is very small, so we conclude that there is significant evidence that the mean home price is **less than $300,000**.
In [32]:
## 1c. A one-sample, right-tail T-test

# Is there any evidence that the mean price is *above* $300,000?

# Now this is a one-sided, right-tail test.
# We calculate the right-tail p-value as:

p_value = stats.t.sf(t, df=len(x)-1)
p_value
Out[32]:
0.9998620319712846
In [ ]:
### Conclusion:
# This p-value is a complement of the previous one, and it is very high. There is **no evidence that the population mean exceeds $300,000**.

# Certainly! If the sample mean is below $300,000, it has no way to support a claim that the population mean is above $300,000.
In [33]:
# 2. A two-sample T-test

# Does the sales price depend on the presence of a pool?
# To answer this question, we have to compare homes with a pool and without a pool. 
# This is a comparison of two populations, so it is a two-sample test.

# First, create the two samples:

pool = H.loc[H["POOL"] == "YES", "SALES_PRICE"]
no_pool = H.loc[H["POOL"] == "NO", "SALES_PRICE"]

# Now perform the two-sample t-test:

stats.ttest_ind(pool, no_pool, equal_var=False)
Out[33]:
TtestResult(statistic=3.428008682163945, pvalue=0.001407868122934726, df=40.54605732690209)
In [34]:
# The argument "equal_var=False" tells Python to perform **Welch's two-sample t-test**, which is the same test used by R's `t.test()` by default.

# The sample means are:
pool.mean(), no_pool.mean()
Out[34]:
(352.1202777777778, 272.39591563786)
In [36]:
# To calculate the confidence interval explicitly:

difference = pool.mean() - no_pool.mean()

se_difference = np.sqrt(
    pool.var(ddof=1) / len(pool)
    + no_pool.var(ddof=1) / len(no_pool)
)

df = (
    (
        pool.var(ddof=1) / len(pool)
        + no_pool.var(ddof=1) / len(no_pool)
    ) ** 2
    /
    (
        (pool.var(ddof=1) / len(pool)) ** 2 / (len(pool) - 1)
        +
        (no_pool.var(ddof=1) / len(no_pool)) ** 2 / (len(no_pool) - 1)
    )
)

t_star = stats.t.ppf(0.975, df)

ci = (
    difference - t_star * se_difference,
    difference + t_star * se_difference
)

ci
Out[36]:
(32.74041556086917, 126.7083087189664)
In [ ]:
# This test compared the mean price of sample X with the mean price of sample Y: homes with the pool and without the pool.
# We find significant evidence that the mean prices are different in the population, and thus, the price does depend on the 
# resence of a pool.

# The difference between mean prices with and without a pool has 95% confidence limits approximately **$32,740 and $126,708**.

# Notice a non-integer number of degrees of freedom. It is calculated by the **Satterthwaite approximation**.
In [37]:
### One-sided, right-tail test

# We can also test whether homes with pools are *more expensive*.
# The alternative hypothesis is:
# mean price of homes with a pool > mean price of homes without a pool

# Calculate the right-tail p-value:

t = result.statistic

p_value = stats.t.sf(t, df)

t, df, p_value
Out[37]:
(3.428008682163945, 40.54605732690209, 0.000703934061467363)
In [ ]:
### Conclusion

# There is significant evidence that homes with a pool are **more expensive, on the average**.
In [39]:
# 3. A two-sample F-test of variances

# This F-test is used to compare variances of two samples and, in particular, to decide which 
# two-sample T-test is appropriate: a test that assumes equal variances or the Satterthwaite approximation.

# We already have the two samples:

pool = H.loc[H["POOL"] == "YES", "SALES_PRICE"]
no_pool = H.loc[H["POOL"] == "NO", "SALES_PRICE"]

# Calculate the sample variances:

pool.var(), no_pool.var()
Out[39]:
(18087.08971706349, 18690.36915219283)
In [40]:
# The F-statistic is the ratio of the two sample variances:

F = pool.var(ddof=1) / no_pool.var(ddof=1)
F
Out[40]:
0.9677224440985125
In [41]:
# The degrees of freedom are:

df1 = len(pool) - 1
df2 = len(no_pool) - 1

df1, df2
Out[41]:
(35, 485)
In [42]:
# For the two-sided F-test, the p-value is:

cdf = stats.f.cdf(F, df1, df2)

p_value = 2 * min(cdf, 1 - cdf)

p_value
Out[42]:
0.9520609027153999
In [43]:
# The 95% confidence interval for the ratio of variances can be calculated as:

alpha = 0.05

lower = F / stats.f.ppf(1 - alpha/2, df1, df2)
upper = F / stats.f.ppf(alpha/2, df1, df2)

lower, upper
Out[43]:
(0.6236526421421951, 1.6674409488087636)
In [ ]:
# The ratio of variances is close to 1, and the p-value is high. So, we conclude that there is **no evidence of different variances**.
# Thus, the equal-variances T-test is justified.
In [44]:
# 4. Parallel boxplots

# We can visualize the differences between the two samples by *parallel boxplots*.
# In R, when we create a scatterplot with the first variable being categorical: plot(POOL, SALES_PRICE)
# In Python, we can create the equivalent boxplots directly:

H.boxplot(
    column="SALES_PRICE",
    by="POOL"
)

plt.title("Sales Price by Pool")
plt.suptitle("")
plt.xlabel("Pool")
plt.ylabel("Sales Price ($1,000s)")
plt.show()
No description has been provided for this image
In [45]:
# The plot supports our findings about the means and variances.
# In particular, we can see that homes with pools tend to have higher sales prices, 
# while the spreads of the two groups appear reasonably similar. This agrees with 
# the results of the two-sample T-test and the F-test of variances.